package com.ctu.gzero.compose
{
	import com.ctu.gzero.container.IController;
	import com.ctu.gzero.edit.ElementRange;
	import com.ctu.gzero.edit.SelectionFormat;
	import com.ctu.gzero.edit.SelectionState;
	import com.ctu.gzero.elements.DOMElement;
	import com.ctu.gzero.elements.ObjectRun;
	import com.ctu.gzero.elements.ParaElement;
	import com.ctu.gzero.elements.RunElement;
	import com.ctu.gzero.elements.TxtRun;
	import com.ctu.gzero.utils.GzLogger;
	
	import flash.display.Sprite;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.text.engine.*;
	
	import mx.collections.ArrayCollection;
	import mx.logging.*;
	import mx.logging.targets.*;
	
	/**
	 *  The Composer class reads DOM and put text lines and display objects to the display 
	 */	
	public class Composer
	{
		private var _controller:IController;
		private var _dom:DOMElement;
		private var _calculatedWidth:Number;
		private var _calculatedHeight:Number;
		private var logger:ILogger;
		private var wrapObjectArr:ArrayCollection;
		
		
		[ArrayElementType("flash.text.engine.TextLine")]
		private var _lineArr:ArrayCollection = new ArrayCollection();
		
		public function Composer()
		{
			logger = GzLogger.getLogger("Composer");
			wrapObjectArr = new ArrayCollection();
		}
		
		public function set dom(val:DOMElement):void
		{
			_dom = val;
		}
		
		public function get dom():DOMElement
		{
			return _dom;
		}
		
		public function get width():Number
		{
			return _controller.width;
		}
		
		public function get height():Number
		{
			return _controller.height;
		}
		
		public function set controller(val:IController):void
		{
			_controller = val;
			_calculatedWidth = _controller.width - _dom.margin * 2;
			_calculatedHeight = _controller.height - dom.margin * 2;
		}
		
		public function get controller():IController
		{
			return _controller;
		}
		
		/**
		 * Compose the DOM first time
		 */
		public function composeDOM():void
		{
			var paraListLength:int = 0;
			
			_dom.normalize();
			
			paraListLength = _dom.paraList.length;
			for(var i:int=0; i<paraListLength; i++)
			{
				composePara(_dom.getPara(i));
			}
		}
		
		
		/**
		 * Compose again the DOM
		 */
		public function reComposeDOM():void
		{
			var para:ParaElement;
			var paraIndex:int;
			var prevTxLine:TxLine = null;
			var firstInvalidTxLine:TxLine;
			var firstInvalidTxLineIndex:int;
			
			para = _dom.firstDamagedPara();
			paraIndex = _dom.paraList.getItemIndex(para);
			
			if(para == null)
				return;
			firstInvalidTxLine = para.firstInvalidTxLine();
			firstInvalidTxLineIndex = para.txLineList.getItemIndex(firstInvalidTxLine);
			
			if(firstInvalidTxLineIndex > 0)
				prevTxLine = para.txLineList.getItemAt(firstInvalidTxLineIndex -1) as TxLine;
			
			var releasedTextLines:ArrayCollection = para.releaseTxLines(firstInvalidTxLine, para.lastTxLine());
			this.removeTextLines(releasedTextLines);
			
			reComposePara(para, prevTxLine);
			
			if(paraIndex < _dom.paraList.length -1)
			{
				for(var i:int=paraIndex+1; i<_dom.paraList.length; i++)
				{
					var followingPara:ParaElement = _dom.getPara(i);
					reComposeParaFollowing(followingPara);
				}
			}
		}
		
		/**
		 * Removes invalid TextLines from display
		 * @param releasedTextLine TextLines to be removed
		 */
		public function removeTextLines(releasedTextLines:ArrayCollection):void
		{
			for(var i:int=0; i<releasedTextLines.length; i++)
			{
				var textLine:TextLine = releasedTextLines.getItemAt(i) as TextLine;
				_controller.container.removeChild(textLine);
			}
		}
		
		/**
		 * Compose again paragraphs right after the damaged paragraph
		 * @param para Target paragraph
		 */
		public function reComposeParaFollowing(para:ParaElement):void
		{
			var vPos:Number = 0;
			var txLine:TxLine = null;
			var prevPara:ParaElement = para.getPreviousPara();
			var prevParaLastTxLine:TxLine = null;
			var prevTxLine:TxLine = null;
			
			if(prevPara == null)
				return;
				
			prevParaLastTxLine = prevPara.lastTxLine();
			vPos = prevParaLastTxLine.vPos;
			
			for(var i:int=0; i<para.txLineList.length; i++)
			{
				txLine = para.txLineList.getItemAt(i) as TxLine;
				if(i == 0)
				{
					txLine.feedLineY = vPos + prevParaLastTxLine.descent + txLine.feedLineHeight - txLine.descent;	
				}
				else
				{
					prevTxLine = para.txLineList.getItemAt(i -1) as TxLine;
					txLine.feedLineY = vPos + prevTxLine.descent + txLine.feedLineHeight - txLine.descent;
				}
				
				
				var newLineSegmentCoord:Array = calculateLineSegmentCoord(para, txLine, _dom.margin, _calculatedWidth);
				if(!txLine.isValidCoord(newLineSegmentCoord))
				{
					var releasedTextLines:ArrayCollection = para.releaseTxLines(txLine, para.lastTxLine());
					this.removeTextLines(releasedTextLines);
					reComposePara(para, prevTxLine);
					break;
				}
				else 
				{
					if(i == 0)
					{
						txLine.vPos = vPos + prevParaLastTxLine.descent + txLine.lineHeight - txLine.descent;	
					}
					else
					{
						txLine.vPos = vPos + prevTxLine.descent + txLine.lineHeight - txLine.descent;
					}
					
					
					for(var j:int=0; j<txLine.segments.length; j++)
					{
						var segmentLine:TextLine = (txLine.segments.getItemAt(j) as TxLineSegment).textLine as TextLine;
						segmentLine.y = txLine.vPos;
					}
					
					vPos = txLine.vPos;
				}
			}
		}
		
		
		/**
		 * Compose damaged paragraph
		 * @param para Target paragraph
		 * @param prevTxLine Previous TxLine in the current target paragraph
		 */
		public function reComposePara(para:ParaElement, prevTxLine:TxLine):void
		{
			var vPos:Number = 0;
			var txLine:TxLine = prevTxLine;
			var prevPara:ParaElement = para.getPreviousPara();
			
			// determine initial vPos
			if(prevTxLine)
			{
				vPos = prevTxLine.vPos;
			} 
			else if(prevPara)
			{
				vPos = prevPara.lastTxLine().vPos;
			}
			else 
			{
				vPos = _dom.margin;
			}
			
			// create TxLines
			while(true)
			{
				txLine = createTxLine(para, txLine, _calculatedWidth, vPos);
				if(txLine == null)
					break;
				
				vPos = txLine.vPos;
				para.txLineList.addItem(txLine);
			}
		}
		
		/**
		 * Compose the paragraph newly. It creates new TextLine objects and calculates the text line's dimension.
		 * @param para The ParaElement to be composed.
		 */
		public function composePara(para:ParaElement):void
		{
			var vPos:Number = 0;
			var txLine:TxLine = null;
			var prevPara:ParaElement = para.getPreviousPara();
			var prevParaLastTxLine:TxLine = null;
			para.createContent();
			
			// determine initial vPos
			if(prevPara)
			{
				prevParaLastTxLine = prevPara.lastTxLine();
				vPos = prevParaLastTxLine.vPos;
			} 
			else 
			{
				vPos = _dom.margin;
			}
			
			// create TxLines
			while(true)
			{
				txLine = createTxLine(para, txLine, _calculatedWidth, vPos);
				if(txLine == null)
					break;
				
				vPos = txLine.vPos;
				para.txLineList.addItem(txLine);
			}
		}
		
		/**
		 * Creates TxLine
		 * @param para The ParaElement to be composed.
		 * @param prevTxLine
		 * @param width Calculated line width.
		 * @param vPos TxLine vertical position(baseLine) 
		 */
		private function createTxLine(para:ParaElement, prevTxLine:TxLine, width:Number, vPos:Number):TxLine
		{
			var txLine:TxLine;
			var feedLine:TextLine;
			var segmentLine:TextLine;
			var textBlock:TextBlock = para.textBlock;
			var lineSegmentCoord:Array;
			var segments:ArrayCollection;
			var feedLineVPos:Number = 0;
			var beginIndex:int;
			var endIndex:int;
			var lineHeight:int;
			var segment:TxLineSegment;
			var prevPara:ParaElement;
			var prevParaLastTxLine:TxLine;
			var lastTxLineSegment:TxLineSegment;
			var lastTextLine:TextLine;
			
			if(prevTxLine)
			{
				lastTxLineSegment = prevTxLine.segments.getItemAt(prevTxLine.segments.length - 1) as TxLineSegment;
				lastTextLine = lastTxLineSegment.textLine;
			}
			else
			{
				prevPara = para.getPreviousPara();
				if(prevPara)
				{
					prevParaLastTxLine = prevPara.lastTxLine();
				}
			}
			
			feedLine = textBlock.createTextLine(lastTextLine, width);
			if(feedLine == null)
				return null;
			
			beginIndex = feedLine.textBlockBeginIndex + para.absoluteStart;
			endIndex = beginIndex + feedLine.rawTextLength - 1;
			lineHeight = findLineHeight(beginIndex, endIndex);
			
			if(vPos == _dom.margin)
			{
				feedLineVPos = vPos + feedLine.ascent;
			}
			else
			{
				// descent leading model
				if(prevTxLine)
				{
					feedLineVPos = vPos + prevTxLine.descent + lineHeight - feedLine.descent;	
				}
				else
				{
					feedLineVPos = vPos + prevParaLastTxLine.descent + lineHeight - feedLine.descent;
				}
			}
			
			feedLine.x = _dom.margin;
			feedLine.y = feedLineVPos;
			
			txLine = new TxLine( _dom.margin, width, para);			
			txLine.feedLineY = feedLine.y;
			txLine.feedLineAscent = feedLine.ascent;
			txLine.feedLineDescent = feedLine.descent;
			txLine.feedLineHeight = lineHeight;
			
			lineSegmentCoord = calculateLineSegmentCoord(para, txLine, _dom.margin, width);
			
			
			txLine.lineSegmentCoord = lineSegmentCoord;
			
			txLine.paraBeginIndex = (prevTxLine)?prevTxLine.paraBeginIndex + prevTxLine.textLength : 0;
			
			if(lineSegmentCoord.length == 1)
			{
				segment = new TxLineSegment(_dom.margin, _dom.margin+width);
				segment.textLine = feedLine;
				segment.lineBeginIndex = 0;
				txLine.segments.addItem(segment);
				feedLine.userData = new Object();
				feedLine.userData.para = para;	
				feedLine.userData.txLine = txLine;
				txLine.textLength = feedLine.rawTextLength;
				txLine.vPos = feedLineVPos;
				txLine.lineHeight = lineHeight;
				txLine.ascent = feedLine.ascent;
				txLine.descent = feedLine.descent;
				return txLine;
			}
			
			segmentLine = feedLine.previousLine;
			textBlock.releaseLines(feedLine, feedLine);
			
			var maxLineHeight:Number = 0;
			var maxDescent:Number = 0;
			var maxAscent:Number = 0;
			var segmentOffset:Number = 0;
			
			for(var i:int =0; i<lineSegmentCoord.length; i++)
			{
				var segmentCoord:Point = lineSegmentCoord[i] as Point;
				segmentLine = textBlock.createTextLine(segmentLine, segmentCoord.y - segmentCoord.x);
				if(segmentLine == null)
					break;
					
				beginIndex = segmentLine.textBlockBeginIndex+para.absoluteStart;
				endIndex = beginIndex + segmentLine.rawTextLength-1;
				lineHeight = findLineHeight(beginIndex, endIndex);
				
				if(maxDescent == 0)
				{
					maxDescent = segmentLine.descent;
				}
				else 
				{
					if(maxDescent < segmentLine.descent)
						maxDescent = segmentLine.descent;
				}
				
				if(maxAscent == 0)
				{
					maxAscent = segmentLine.ascent;
				}
				else 
				{
					if(maxAscent < segmentLine.ascent)
						maxAscent = segmentLine.ascent;
				}
				
				if(maxLineHeight == 0)
				{
					maxLineHeight = lineHeight;
				} 
				else 
				{
					if(maxLineHeight < lineHeight)
						maxLineHeight = lineHeight;
				}
				
				segmentLine.x = segmentCoord.x;
				
				segment = new TxLineSegment(segmentCoord.x,  segmentCoord.y);
				segment.textLine = segmentLine;
				segment.lineBeginIndex = segmentOffset;
				txLine.segments.addItem(segment);
				
				segmentLine.userData = new Object();
				segmentLine.userData.para = para;	
				segmentLine.userData.txLine = txLine;
				
				segmentOffset += segmentLine.rawTextLength;
				
				var lastCharCode:int = para.getCharCodeAtPosition(endIndex - beginIndex + segmentLine.textBlockBeginIndex);
				
				// line break must break entire txLine
				if(lastCharCode == 0x2028)
					break;
			}
			
			// adjust baselines of the segments
			if(vPos == _dom.margin)
				txLine.vPos = _dom.margin + maxAscent;
			else
			{
				// descent leading model
				if(prevTxLine)
				{
					txLine.vPos = vPos + prevTxLine.descent + maxLineHeight - maxDescent;	
				}
				else 
				{
					txLine.vPos = vPos + prevParaLastTxLine.descent + maxLineHeight - maxDescent;
				}
			}
				
			for(var j:int; j<txLine.segments.length; j++)
			{
				segmentLine = (txLine.segments.getItemAt(j) as TxLineSegment).textLine as TextLine;
				segmentLine.y = txLine.vPos;
			}
			
			txLine.textLength = segmentOffset;
			txLine.lineHeight = maxLineHeight;
			txLine.ascent = maxAscent;
			txLine.descent = maxDescent;
			
			return txLine;
		}
		
		/**
		 * Calculate and return the segment coordinates for given TxLine and ObjectRuns over it.
		 * @param curPara Current paragraph
		 * @param txLine Target TxLine
		 * @param x TxLine x
		 * @param width TxLine width
		 * @return Line segment coordinates
		 */
		private function calculateLineSegmentCoord(curPara:ParaElement, txLine:TxLine, x:Number, width:Number):Array
		{
			var segmentCoodList:Array = new Array();
			var segmentCoord:Point;
			var intersectRect:Point;
			var curParaIndex:int = _dom.getParaIndex(curPara);
			var wrapCoordList:Array = new Array();
			
			for(var i:int=0; i<=curParaIndex; i++)
			{
				var para:ParaElement = _dom.getPara(i);
				var prevPara:ParaElement = para.getPreviousPara();
				
				for(var j:int =0; j<para.wrapObjectList.length; j++)
				{
					var objectRun:ObjectRun = para.wrapObjectList.getItemAt(j) as ObjectRun;
					var prevParaLastTxLine:TxLine = null;
					var objY:Number;
					var objX:Number;
					
					if(prevPara != null)
					{
						prevParaLastTxLine = prevPara.lastTxLine();
						objY = prevParaLastTxLine.vPos + prevParaLastTxLine.descent + objectRun.top + objectRun.margin;
						objX = x + objectRun.left + objectRun.margin;
					}
					else 
					{
						objY = _dom.margin + objectRun.top + objectRun.margin;
						objX = x + objectRun.left + objectRun.margin;
					}
					
					objectRun.x = objX;
					objectRun.y = objY;
					
					var currentLineTop:Number = txLine.feedLineY - txLine.feedLineAscent;
					var currentLineBottom:Number = txLine.feedLineY + txLine.feedLineDescent;
					
					if(!(currentLineTop > objY - objectRun.margin && currentLineTop > objY + objectRun.height + objectRun.margin) && 
							!(currentLineBottom < objY - objectRun.margin && currentLineBottom < objY + objectRun.height + objectRun.margin))
					{
						intersectRect = new Point(objX - objectRun.margin, objX + objectRun.width + objectRun.margin);
						wrapCoordList.push(intersectRect);
					}
				}	
			}
			
			wrapCoordList.sortOn("x", Array.NUMERIC);
			
			// determine segment coordinates
			var startX:Number = x;
			
			for(var idx:int; idx<wrapCoordList.length; idx++)
			{
				intersectRect = wrapCoordList[idx] as Point;
				
				if(startX < intersectRect.x)
				{
					segmentCoord = new Point(startX, intersectRect.x);
					segmentCoodList.push(segmentCoord);
				}
				startX = intersectRect.y;
			}
			
			if(startX < x+width)
			{
				segmentCoord = new Point(startX, x+width);
				segmentCoodList.push(segmentCoord);
			}
			
			//logger.debug("wrapCoordList:"+wrapCoordList+" segmentCoodList:"+segmentCoodList);
			return segmentCoodList;
		}
		
		
		/**
		 * Create and add ObjectRuns to display first time
		 */
		private function createObjects():void
		{
			for(var i:int=0; i<_dom.paraList.length; i++)
			{
				var para:ParaElement = _dom.getPara(i);
				
				for(var j:int =0; j<para.wrapObjectList.length; j++)
				{
					var objectRun:ObjectRun = para.wrapObjectList.getItemAt(j) as ObjectRun;
					var box:Sprite = new Sprite();
					box.graphics.clear();
					box.graphics.beginFill(objectRun.backgroundColor, objectRun.alpha); 
					box.graphics.drawRoundRectComplex(objectRun.x, objectRun.y, objectRun.width, objectRun.height, 0, 0, 0, 0);
					box.graphics.endFill();
					objectRun.obj = box;
					_controller.container.addChild(box);
				}
			}
		}
		
		/**
		 * Update ObjectRun's dimension
		 * 
		 */
		private function updateObjects():void
		{
			for(var i:int=0; i<_dom.paraList.length; i++)
			{
				var para:ParaElement = _dom.getPara(i);
				
				for(var j:int =0; j<para.wrapObjectList.length; j++)
				{
					var objectRun:ObjectRun = para.wrapObjectList.getItemAt(j) as ObjectRun;
					var box:Sprite = objectRun.obj as Sprite;
					box.graphics.clear();
					box.graphics.beginFill(objectRun.backgroundColor, objectRun.alpha); 
					box.graphics.drawRoundRectComplex(objectRun.x, objectRun.y, objectRun.width, objectRun.height, 0, 0, 0, 0);
					box.graphics.endFill();
				}
			}
		}
		
		/**
		 * Compose and add first created TextLine's to display
		 */
		public function updateDisplay():void
		{
			var _paraListLength:int = _dom.paraList.length;
			
			composeDOM();
			
			for(var i:int=0; i<_paraListLength; i++)
			{
				var para:ParaElement = _dom.getPara(i);
				var textLineList:Array = para.getTextLines();
				
				for(var j:int=0; j<textLineList.length; j++)
				{
					_controller.container.addChild(textLineList[j] as TextLine);	
				}
			}
			
			createObjects();
		}
		
		
		/**
		 * Compose again and update or add TextLine's to display
		 */
		public function reUpdateDisplay():void
		{
			var _paraListLength:int = _dom.paraList.length;
			
			reComposeDOM();
			
			for(var i:int=0; i<_paraListLength; i++)
			{
				var para:ParaElement = _dom.getPara(i);
				var textLineList:Array = para.getTextLines();
				
				for(var j:int=0; j<textLineList.length; j++)
				{
					// only newly broken lines are added 
					if(_controller.container.contains(textLineList[j] as TextLine) == false)
					{
						_controller.container.addChild(textLineList[j] as TextLine);
					}
				}
			}
			
			updateObjects();
		}
		
		public function findLineAtPosition(absolutePosition:int):TextLine
		{
			var textLine:TextLine;
			var para:ParaElement;			
			para = _dom.findParaAtPosition(absolutePosition);
			textLine = para.textBlock.getTextLineAtCharIndex(absolutePosition - para.absoluteStart);
			return textLine;
		}
		
		public function findTxLineAtPosition(absolutePosition:int):TxLine
		{
			var txLine:TxLine;
			var para:ParaElement;			
			para = _dom.findParaAtPosition(absolutePosition);
			txLine = para.getTxLineAtCharIndex(absolutePosition - para.absoluteStart);
			
			return txLine;
		}
		
		public function updateSelection(selectionState:SelectionState):void
		{
			logger.debug("selectionState:"+selectionState.anchorPosition+"/"+selectionState.activePosition);
			var prevPara:ParaElement;
			var curPara:ParaElement;
			var prevTextLine:TextLine;
			var curTextLine:TextLine;
			var prevTxLine:TxLine;
			var curTxLine:TxLine;
			var atomIndex:int;
			var atomBound:Rectangle;
			var xPos:Number;
			var yPos:Number;
			var cursorHeight:Number;
			var _width:Number;	
			var _height:Number		
			
			var positionStart:Number;
			var positionEnd:Number;
			
			if(selectionState.anchorPosition == selectionState.activePosition)
			{
				if(selectionState.endFlag)
				{
					prevPara= _dom.findParaAtPosition(selectionState.anchorPosition -1);
					prevTextLine = findLineAtPosition(selectionState.anchorPosition-1);
					atomIndex = prevTextLine.getAtomIndexAtCharIndex(selectionState.anchorPosition -1 - prevPara.absoluteStart);
					atomBound = prevTextLine.getAtomBounds(atomIndex);
					xPos = 	atomBound.x + atomBound.width + prevTextLine.x;
					yPos = prevTextLine.y - prevTextLine.ascent;
					cursorHeight = prevTextLine.height;
					prevTextLine.flushAtomData();
				}
				else 
				{
					curPara= _dom.findParaAtPosition(selectionState.anchorPosition);
					curTextLine = findLineAtPosition(selectionState.anchorPosition);
					atomIndex = curTextLine.getAtomIndexAtCharIndex(selectionState.anchorPosition - curPara.absoluteStart);
					atomBound = curTextLine.getAtomBounds(atomIndex);
					xPos = 	atomBound.x + curTextLine.x;
					yPos = curTextLine.y - curTextLine.ascent;
					cursorHeight = curTextLine.height;	
					curTextLine.flushAtomData();
				}
				
				_controller.showCursor(xPos, yPos, 1, cursorHeight);
			}
			else
			{
				logger.debug("\n anchorPosition : ["+selectionState.anchorPosition+"] \n	activePosition : ["+selectionState.activePosition+"] \n ");
				if(selectionState.anchorPosition > selectionState.activePosition)
				{
					positionStart = selectionState.activePosition;
					positionEnd   =  selectionState.anchorPosition
				}
				else
				{
					positionStart = selectionState.anchorPosition;
					positionEnd   =  selectionState.activePosition;
				}
				
				prevPara = _dom.findParaAtPosition(positionStart);
				curPara = _dom.findParaAtPosition(positionEnd);
												
				prevTextLine = findLineAtPosition(positionStart);
				curTextLine   = findLineAtPosition(positionEnd);
								
				prevTxLine = prevTextLine.userData.txLine;
				curTxLine   = curTextLine.userData.txLine;
				
				var _tmpTxLine:TxLine = prevTxLine;
				var _segmentIndex:int = 0;
				var segmentLine:TextLine = null;
				
				while(_tmpTxLine != null)
				{					
					if(_tmpTxLine == prevTxLine)
					{
						atomIndex = prevTextLine.getAtomIndexAtCharIndex(positionStart - prevPara.absoluteStart);
						atomBound = prevTextLine.getAtomBounds(atomIndex);
						segmentLine = (_tmpTxLine.segments.getItemAt(_segmentIndex) as TxLineSegment).textLine as TextLine;
						if(segmentLine.x > atomBound.x + prevTextLine.x) 
						{
							xPos = segmentLine.x;						
						} 
						else 
						{
							xPos = 	atomBound.x + prevTextLine.x;
						}						
						prevTextLine.flushAtomData();
					} 
					else
					{
						segmentLine = (_tmpTxLine.segments.getItemAt(_segmentIndex) as TxLineSegment).textLine as TextLine;
						xPos = segmentLine.x;						
					}
					yPos = getYPos(_tmpTxLine);
					
					_segmentIndex = getCurrentSegmentIndex(_tmpTxLine, _segmentIndex, xPos);					
					
					if(_tmpTxLine == curTxLine)
					{
						atomIndex = curTextLine.getAtomIndexAtCharIndex(positionEnd - curPara.absoluteStart);
						atomBound = curTextLine.getAtomBounds(atomIndex);
						
						segmentLine = (_tmpTxLine.segments.getItemAt(_segmentIndex) as TxLineSegment).textLine as TextLine;
						if(segmentLine.x + segmentLine.width > curTextLine.x + atomBound.x) 
						{
							_width = curTextLine.x + atomBound.x - xPos;	
							_segmentIndex = _tmpTxLine.segments.length;		//마지막 위치									
						} 
						else 
						{
							_width = segmentLine.x + segmentLine.width - xPos;
						}
						curTextLine.flushAtomData();						
					}
					else 
					{						 
						segmentLine = (_tmpTxLine.segments.getItemAt(_segmentIndex) as TxLineSegment).textLine as TextLine;
						_width = segmentLine.x + segmentLine.width - xPos;
					}
					_height = _tmpTxLine.vPos + _tmpTxLine.descent - yPos;
					
					_controller.drawPointSelection(new SelectionFormat(0x0000FF,0.3,"test"), xPos,yPos,_width,_height);
					logger.debug("\n xPos : ["+xPos+"] \n	yPos : ["+yPos+"] \n _width : ["+_width+"] \n _height : ["+_height+"] \n");
										
					if(_tmpTxLine != curTxLine) {
						if(_segmentIndex < _tmpTxLine.segments.length-1) 
						{
							_segmentIndex+=1;			
						}
						else
						{
							_tmpTxLine = getNextTxLine(_tmpTxLine);
							_segmentIndex = 0;
						}
					}
					else
					{					 
						if(_segmentIndex < _tmpTxLine.segments.length-1) 
						{
							_segmentIndex+=1;			
						} 
						else
						{
							_tmpTxLine = null;
						}
					}					
				}	
			}
		}
		
		public function showSelection(selectionState:SelectionState):void
		{
			updateSelection(selectionState);
		}
		
		public function interactionManagerChanged():void
		{
			_controller.interactionManagerChanged(_dom.interactionManager);
		}
		
		/**
		 * 주인 beginIndx, endIdx 를 가지고 해당 라인의 부분의 최대 lineHeight 를 리턴
		 * 하나의 라인에 최소한 하나의 TxtRun 있다고 가정
		 */
		private function findLineHeight(beginIdx:int, endIdx:int):Number 
		{
			var range:ElementRange = ElementRange.createElementRange(_dom, beginIdx, endIdx);
			var leaf:RunElement = range.firstRun;
			var lastLeaf:RunElement = range.lastRun;
			var lastPara:ParaElement = range.lastPara;
			
			var maxLineHeight:Number = 0;
			
			if(leaf == null)
				throw new Error("Composer findLineHeight: leaf is null");
			
			if(leaf != null && leaf.type == "TxtRun")
				maxLineHeight = convertHeight({fontSize:(leaf as TxtRun).fontSize, lineHeight: (leaf as TxtRun).lineHeight});
				
			
			
			if(leaf != null && lastLeaf != null)
			{
				if(leaf == lastLeaf)
					return maxLineHeight;
			}
			
			var cnt:int = 0;
			while(true)
			{
				leaf = leaf.getNextTextLeaf(lastPara);
				if(leaf != null && leaf.type == "TxtRun")
				{
					var lineHeight:Number = convertHeight({fontSize:(leaf as TxtRun).fontSize, lineHeight: (leaf as TxtRun).lineHeight});
					if(lineHeight > maxLineHeight)
						maxLineHeight = lineHeight;
				}
				
				if(leaf == null || leaf == lastLeaf)
					break;
					
				cnt++;
				if(cnt > 5)
					break;
			}
			
			return maxLineHeight;
		}
		
		/**
		 * 주어인 characterFormat에서 계산된 lineHeight를 pixel로 리턴  
		 */
		private function convertHeight(textStyle:Object):Number
		{
			var lineHeight:Number = 0;
			var fontSize:Number = (textStyle.fontSize != 0)? (textStyle.fontSize as Number):12;
			if(textStyle.lineHeight == null)
			{
				lineHeight = fontSize * 1.2;
			} 
			else if(textStyle.lineHeight is Number)
			{
				lineHeight = (textStyle.lineHeight as Number);
			} 
			else if(textStyle.lineHeight is String)
			{
				lineHeight = fontSize * percentToNumber(textStyle.lineHeight as String)/100;
			}
			return lineHeight;
		}
		
		/**
		 * String 으로 주어진 percent를 Number로 리턴
		 */
		private function percentToNumber(str:String):Number
		{
			var percentStr:String = str.substring(0, str.length-1);
			var percent:Number = parseInt(percentStr);
			return percent;
		}
		
		public function hideSelection():void
		{
			_controller.hideSelectionShape();
		}
		
		/**
		 *  TxLine 의 y 위치를 리턴
		 */
		private function getYPos(txLine:TxLine):int
		{
			var _prevTxLine:TxLine;
			var _y:int = -1;
			
			
			if(txLine==null)
			{
				return _y;
			}
			
			var para:ParaElement = txLine.para;
			
			for(var i:int = 0, len:int=para.txLineList.length;i< len;i++)
			{
				if(para.txLineList.getItemAt(i) == txLine)
				{
					if(i!=0) {
						_prevTxLine = para.txLineList.getItemAt(i-1) as TxLine;						
						return _prevTxLine.vPos + _prevTxLine.descent;
					}
					else 
					{
						para = para.getPreviousPara();
						if(para==null)
						{
							return  0;							
						}
						else 
						{
							len = para.txLineList.length;
							_prevTxLine = para.txLineList.getItemAt(len-1) as TxLine;
							return _prevTxLine.vPos + _prevTxLine.descent;													
						}
					}					
				}
				
			}
			return _y;
		}
		/**
		 *  TxLine 다음 TxLine를 리턴
		 */
		private function getNextTxLine(txLine:TxLine):TxLine
		{
			var _nextTxLine:TxLine = null;
			var para:ParaElement = txLine.para;
			
			for(var i:int = 0, len:int=para.txLineList.length;i< len;i++)
			{
				if(para.txLineList.getItemAt(i) == txLine)
				{
					if(i!=(len-1)) {
						_nextTxLine = para.txLineList.getItemAt(i+1) as TxLine;						
						return _nextTxLine;
					}
					else 
					{
						para = para.getNextPara();
						if(para==null)
						{		
							return null;
						}
						else 
						{
							len = para.txLineList.length;
							_nextTxLine = para.txLineList.getItemAt(0) as TxLine;
							return 	_nextTxLine;
						}
					}					
				}				
			}			
			return _nextTxLine;			
		}
		
		private function getCurrentSegmentIndex(_tmpTxLine:TxLine, _segmentIndex:int, xPos:int):int 
		{
			var segmentLine:TextLine = null;
			
			for(var i:int = _segmentIndex, len:int = _tmpTxLine.segments.length;i<len;i++) {						
				segmentLine = (_tmpTxLine.segments.getItemAt(i) as TxLineSegment).textLine as TextLine;
				if(segmentLine.x + segmentLine.width > xPos) {												
					break;
				}
			}
			return i; 
		}

	}		
}